/**
 * Copyright 2018-2019 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.modulelink.module;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.jianggujin.modulelink.action.JAction;
import com.jianggujin.modulelink.action.JActionWrapper;
import com.jianggujin.modulelink.action.JActiveAction;
import com.jianggujin.modulelink.config.JModuleConfig;
import com.jianggujin.modulelink.exception.JModuleLinkException;
import com.jianggujin.modulelink.exception.JModuleNotFoundException;
import com.jianggujin.modulelink.support.JDisposable;
import com.jianggujin.modulelink.support.JInitializing;
import com.jianggujin.modulelink.util.JAssert;
import com.jianggujin.modulelink.util.JLogFactory;
import com.jianggujin.modulelink.util.JLogFactory.JLog;
import com.jianggujin.modulelink.util.JModuleClassLoader;
import com.jianggujin.modulelink.util.JStringUtils;

/**
 * 默认模块
 * 
 * @author jianggujin
 *
 */
public class JDefaultModule<C extends JModuleConfig> implements JModule<C> {
    private static final JLog logger = JLogFactory.getLog(JDefaultModule.class);
    /**
     * 模块的配置信息
     */
    private C moduleConfig;
    /**
     * 模块的类加载器
     */
    private JModuleClassLoader moduleClassLoader;
    /**
     * 默认Action
     */
    private JActionWrapper defaultActionWrapper;
    /**
     * 模块中的Action
     */
    private Map<String, JActionWrapper> actionWrappers;
    /**
     * 模块事件发布器
     */
    private JModuleEventPublisher publisher;
    /**
     * 生命周期状态
     */
    private JModuleState moduleState;
    private JDisposable disposable;

    public JDefaultModule() {
        this.moduleState = JModuleState.CREATED;
    }

    @Override
    public Set<String> getActionNames() {
        return this.actionWrappers.keySet();
    }

    @Override
    public Collection<JActionWrapper> getActionWrappers() {
        return this.actionWrappers.values();
    }

    @Override
    public Collection<Object> getActions() {
        Collection<JActionWrapper> values = this.getActionWrappers();
        List<Object> actions = new ArrayList<Object>(values.size());
        for (JActionWrapper wrapper : values) {
            actions.add(wrapper.getAction());
        }
        return actions;
    }

    @Override
    public String getDefaultActionName() {
        return getDefaultActionWrapper().getName();
    }

    @Override
    public Object getDefaultAction() {
        return getDefaultActionWrapper().getAction();
    }

    @Override
    public JActionWrapper getDefaultActionWrapper() {
        JActionWrapper wrapper = optDefaultActionWrapper();
        if (wrapper == null) {
            throw new JModuleNotFoundException("could not found default action");
        }
        return wrapper;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getDefaultAction(Class<T> clazz) {
        return (T) getDefaultAction();
    }

    @Override
    public String optDefaultActionName() {
        JActionWrapper wrapper = this.optDefaultActionWrapper();
        return wrapper == null ? null : wrapper.getName();
    }

    @Override
    public Object optDefaultAction() {
        JActionWrapper wrapper = this.optDefaultActionWrapper();
        return wrapper == null ? null : wrapper.getAction();
    }

    @Override
    public JActionWrapper optDefaultActionWrapper() {
        return this.defaultActionWrapper;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T optDefaultAction(Class<T> clazz) {
        Object action = this.optDefaultAction();
        return (T) (action == null ? null : action);
    }

    @Override
    public Object getAction(String name) {
        return this.getActionWrapper(name).getAction();
    }

    @Override
    public JActionWrapper getActionWrapper(String name) {
        JActionWrapper wrapper = this.optActionWrapper(name);
        if (wrapper == null) {
            throw new JModuleNotFoundException("could not found action with name:" + name);
        }
        return wrapper;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getAction(String name, Class<T> clazz) {
        return (T) this.getAction(name);
    }

    @Override
    public Object optAction(String name) {
        JActionWrapper wrapper = this.optActionWrapper(name);
        return wrapper == null ? null : wrapper.getAction();
    }

    @Override
    public JActionWrapper optActionWrapper(String name) {
        JAssert.checkNotNull(name, "action name must not be null");
        return this.actionWrappers.get(name);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T optAction(String name, Class<T> clazz) {
        Object action = this.optAction(name);
        return (T) (action == null ? null : action);
    }

    @Override
    public boolean hasAction(String name) {
        JAssert.checkNotNull(name, "action name must not be null");
        return this.actionWrappers.containsKey(name);
    }

    @Override
    public boolean hasDefaultAction() {
        return this.defaultActionWrapper != null;
    }

    @Override
    public Object invokeAction(String name, Object in) {
        return invokeAction(getActionWrapper(name), in);
    }

    @Override
    public Object invokeDefaultAction(Object in) {
        return invokeAction(getDefaultActionWrapper(), in);
    }

    /**
     * 执行Action
     * 
     * @param action 需要执行的Action
     * @param in     输入对象
     * @return 执行结果
     */
    private Object invokeAction(JActionWrapper wrapper, Object in) {
        if (wrapper.getExecuteMethod() != null) {
            try {
                return wrapper.getExecuteMethod().invoke(wrapper.getAction(), in);
            } catch (Exception e) {
                throw new JModuleLinkException(
                        JStringUtils.format("invoke action [{}] default execute method error.", wrapper.getName()), e);
            }
        }
        if (wrapper.getAction() instanceof JAction) {
            return ((JAction) wrapper.getAction()).execute(in);
        }
        throw new JModuleLinkException("action [{}] unsupported default execute method.", wrapper.getName());
    }

    @Override
    public synchronized void initialize() {
        JAssert.checkState(JModuleState.CREATED == this.moduleState, "module state is not created");
        // 临时置为激活，不发送任何通知
        this.moduleState = JModuleState.ACTIVED;
        try {
            for (Entry<String, JActionWrapper> entry : this.actionWrappers.entrySet()) {
                Object action = entry.getValue().getAction();
                if (action instanceof JInitializing) {
                    ((JInitializing) action).initialize();
                }
            }
        } finally {
            this.moduleState = JModuleState.CREATED;
        }
        this.moduleState = JModuleState.INITIALIZED;
        this.getModuleEventPublisher().publishStateChangedEvent(this.moduleConfig, JModuleState.CREATED,
                this.moduleState);
    }

    @Override
    public synchronized void active() {
        JAssert.checkState(
                JModuleState.INITIALIZED == this.moduleState || JModuleState.UNACTIVED == this.moduleState
                        || JModuleState.ACTIVED == this.moduleState,
                "module state is not initialized、actived or unActived.");
        JModuleState oldModuleState = this.moduleState;
        this.moduleState = JModuleState.ACTIVED;
        for (Entry<String, JActionWrapper> entry : this.actionWrappers.entrySet()) {
            Object action = entry.getValue().getAction();
            if (action instanceof JActiveAction) {
                try {
                    ((JActiveAction) action).active();
                } catch (Exception e) {
                    logger.error(JStringUtils.format("active action has error, module:{}, action:{}",
                            this.moduleConfig.getModuleName(), entry.getValue().getName()), e);
                }
            }
        }
        this.getModuleEventPublisher().publishStateChangedEvent(this.moduleConfig, oldModuleState, this.moduleState);
    }

    @Override
    public synchronized void unActive() {
        JAssert.checkState(
                JModuleState.INITIALIZED == this.moduleState || JModuleState.ACTIVED == this.moduleState
                        || JModuleState.UNACTIVED == this.moduleState,
                "module state is not initialized、unActived or actived.");
        JModuleState oldModuleState = this.moduleState;
        // 临时置为激活，不发送任何通知
        this.moduleState = JModuleState.ACTIVED;
        for (Entry<String, JActionWrapper> entry : this.actionWrappers.entrySet()) {
            Object action = entry.getValue().getAction();
            if (action instanceof JActiveAction) {
                try {
                    ((JActiveAction) action).unActive();
                } catch (Exception e) {
                    logger.error(JStringUtils.format("unActive action has error, module:{}, action:{}",
                            this.moduleConfig.getModuleName(), entry.getValue().getName()), e);
                }
            }
        }
        this.moduleState = JModuleState.UNACTIVED;
        this.getModuleEventPublisher().publishStateChangedEvent(this.moduleConfig, oldModuleState, this.moduleState);
    }

    @Override
    public synchronized void destroy() {
        JAssert.checkState(
                this.moduleState.ordinal() > JModuleState.CREATED.ordinal()
                        && this.moduleState.ordinal() < JModuleState.DESTROYED.ordinal(),
                "module state is not initialized、actived or unActived.");
        JModuleState oldModuleState = this.moduleState;
        // 临时置为激活，不发送任何通知
        this.moduleState = JModuleState.ACTIVED;
        for (Entry<String, JActionWrapper> entry : this.actionWrappers.entrySet()) {
            Object action = entry.getValue().getAction();
            if (action instanceof JDisposable) {
                try {
                    ((JDisposable) action).destroy();
                } catch (Exception e) {
                    logger.error(JStringUtils.format("destroy action has error, module:{}, action:{}",
                            this.moduleConfig.getModuleName(), entry.getValue().getName()), e);
                }
            }
        }
        if (this.disposable != null) {
            try {
                this.disposable.destroy();
            } catch (Exception e) {
                logger.error(JStringUtils.format("disposable destroy has error, module:{}",
                        this.moduleConfig.getModuleName()), e);
            }
        }
        this.moduleState = JModuleState.DESTROYED;
        this.getModuleEventPublisher().publishStateChangedEvent(this.moduleConfig, oldModuleState, this.moduleState);
    }

    @Override
    public JModuleState getModuleState() {
        return this.moduleState;
    }

    @Override
    public C getModuleConfig() {
        return moduleConfig;
    }

    @Override
    public ClassLoader getModuleClassLoader() {
        return moduleClassLoader;
    }

    public void setModuleConfig(C moduleConfig) {
        this.moduleConfig = moduleConfig;
    }

    public void setModuleClassLoader(JModuleClassLoader moduleClassLoader) {
        this.moduleClassLoader = moduleClassLoader;
    }

    public void setDefaultActionWrapper(JActionWrapper defaultActionWrapper) {
        this.defaultActionWrapper = defaultActionWrapper;
    }

    public void setActionWrappers(Map<String, JActionWrapper> actionWrappers) {
        this.actionWrappers = actionWrappers;
    }

    public void setListeners(List<JModuleListener> listeners) {
        this.publisher = new JModuleEventPublisher(listeners);
    }

    public void setDisposable(JDisposable disposable) {
        this.disposable = disposable;
    }

    private JModuleEventPublisher getModuleEventPublisher() {
        if (this.publisher == null) {
            return JModuleEventPublisher.getInstance();
        }
        return this.publisher;
    }
}
